#include "TeminalPlugin.h"

TeminalPlugin::TeminalPlugin(QObject* parent)
{
    // 初始化插件信息
    this->pluginInfo.pluginName    = PLUGIN_NAME;
    this->pluginInfo.pluginVersion = PLUGIN_VERSION;
    this->pluginInfo.pluginQmlRes  = PLUGIN_QMLRES;
    this->pluginInfo.pluginLogo    = QIcon(PLUGIN_LOGO);

    this->moveToThread(this);
    connect(this, &TeminalPlugin::OpenSerialPortRequested, this, &TeminalPlugin::HandleOpenSerialPort);
    connect(this, &TeminalPlugin::SerialPortTxRequested, this, &TeminalPlugin::HandleSerialPortTx);
}

void TeminalPlugin::PluginInitAtThreadHook()
{
    this->refreshTimer = new QTimer();
    connect(this->refreshTimer, &QTimer::timeout, this, &TeminalPlugin::RefreshDataToQml);
    this->refreshTimer->start(this->refreshTimeOut);

    this->refreshPortTimer = new QTimer();
    connect(this->refreshPortTimer, &QTimer::timeout, this, &TeminalPlugin::RefreshSerialPort);
    this->refreshPortTimer->start(1000);

    this->RefreshSerialPort();
}

int TeminalPlugin::PluginThreadLoop()
{
    int     icnt = 0;
    int     jcnt = 0;
    QString tmpString;

    for (MySerialPort* port : this->serialPort)
    {
        if (port->serialPort.isOpen())
        {
            if (port->serialPort.bytesAvailable())
            {
                QByteArray data = port->serialPort.readAll();
                QString    dataStr;
                QMetaObject::invokeMethod(qmlObj, "portTxRxNum", QVariant::fromValue(port->serialPort.portName()),
                                          QVariant::fromValue(0), QVariant::fromValue(data.length()));
                // TO:DO:processing data
                if (port->pattern == 0)
                {
                    for (; icnt < data.length(); icnt++)
                    {
                        if (data[icnt] == '\n')
                        {
                            if (port->timeStamp)
                            {
                                if (port->hexReception)
                                {
                                    data.insert(icnt, "0x0A ");
                                    icnt += 5;
                                }
                                tmpString = QDateTime::currentDateTime().toString("HH:mm:ss>");
                                data.insert(icnt + 1, tmpString.toUtf8());
                                icnt += 9;
                                continue;
                            }
                        }
                        if (port->hexReception)
                        {
                            if (port->hexLineFeed && data[icnt] == '\n')
                            {
                                data.insert(icnt, "0A ");
                                icnt += 3;
                                continue;
                            }
                            tmpString = QByteArray(1, data.at(icnt)).toHex().toUpper();
                            data.remove(icnt, 1);                   // 删除当前字符
                            data.insert(icnt, tmpString.toUtf8());  // 插入16进制字符
                            if (tmpString.length() == 1)
                            {
                                data.insert(icnt, '0');
                            }
                            icnt += 2;
                            data.insert(icnt, ' ');  // 插入空格
                        }
                    }
                    dataStr = QString(data).replace('\n', "<br>");
                    port->pushRxData(dataStr);
                }
                else if (port->pattern == 1)
                {
                    dataStr = QString(data);
                    port->pushRxData(dataStr);
                }
                else if (port->pattern == 2)
                {
                    QString tmpStr;

                    icnt    = 0;
                    dataStr = QString(data);

                    for (auto& line : port->line)
                    {
                        int index = dataStr.lastIndexOf(line.lineName);
                        if (index != -1)
                        {
                            tmpStr = dataStr.mid(index + line.lineName.length());
                            QRegularExpression      re("(-?\\d+(\\.\\d+)?)");
                            QRegularExpressionMatch match = re.match(tmpStr);

                            if (match.hasMatch())
                            {
                                port->pushLineData(line, match.captured(0).toFloat());
                                // qDebug() << "Matched number:" << match.captured(0).toFloat();
                            }
                        }
                    }
                }
            }
        }
    }

    msleep(10);
    return SBT_NERR;
}

void TeminalPlugin::RefreshSerialPort(void)
{
    QSet<QString> availablePorts;
    foreach (const QSerialPortInfo& info, QSerialPortInfo::availablePorts())
    {
        availablePorts.insert(info.portName());
    }

    for (int icnt = this->serialPort.size() - 1; icnt >= 0; --icnt)
    {
        MySerialPort* port = this->serialPort[icnt];
        if (!availablePorts.contains(port->serialPort.portName()))
        {
            QMetaObject::invokeMethod(qmlObj, "portDelete",
                                      Q_ARG(QVariant, QVariant::fromValue(port->serialPort.portName())));
            port->serialPort.close();
            this->serialPort.removeAt(icnt);
            delete port;
        }
    }

    foreach (const QSerialPortInfo& info, QSerialPortInfo::availablePorts())
    {
        bool isNewPort = true;
        for (MySerialPort* port : this->serialPort)
        {
            if (port->serialPort.portName() == info.portName())
            {
                isNewPort = false;
                break;
            }
        }

        if (isNewPort)
        {
            MySerialPort* port = new MySerialPort();
            port->serialPort.setPort(info);
            if (port->serialPort.open(QIODevice::ReadWrite))
            {
                this->serialPort.append(port);
                QMetaObject::invokeMethod(qmlObj, "portAdd", QVariant::fromValue(port->serialPort.portName()));
                port->serialPort.close();
            }
            else
            {
                delete port;
            }
        }
    }
}

void TeminalPlugin::RefreshDataToQml()
{
    this->refreshTimer->stop();

    this->timeStamp++;

    for (MySerialPort* port : this->serialPort)
    {
        if (port->pattern == 2 && port->serialPort.isOpen())
        {
            float aveData = 0;
            float icnt    = 0;

            for (auto& lineData : port->line)
            {
                QMetaObject::invokeMethod(qmlObj, "portLineAppendData",
                                          QVariant::fromValue(port->serialPort.portName()),
                                          QVariant::fromValue(lineData.lineName), QVariant::fromValue(timeStamp),
                                          QVariant::fromValue(lineData.data));
                aveData += lineData.data;
                icnt++;
            }

            if (icnt != 0)
            {
                QMetaObject::invokeMethod(qmlObj, "portLineYAxisScaleSet",
                                          QVariant::fromValue(port->serialPort.portName()),
                                          QVariant::fromValue(aveData / icnt));
            }

            if (this->refreshTimeOut != 10)
            {
                this->refreshTimeOut = 10;
            }
        }
        else if (port->serialPort.isOpen())
        {
            if (port->rxDataBuffer.length())
            {
                QMetaObject::invokeMethod(qmlObj, "portInsertTextBeforeCursor",
                                          QVariant::fromValue(port->serialPort.portName()),
                                          QVariant::fromValue(port->popRxData()));
            }
            if (this->refreshTimeOut != 50)
            {
                this->refreshTimeOut = 50;
            }
        }
    }

    this->refreshTimer->start(refreshTimeOut);
}

bool TeminalPlugin::OpenSerialPort(QString portName)
{
    emit this->OpenSerialPortRequested(portName);
    return SBT_TRUE;
}

void TeminalPlugin::CloseSerialPort(QString portName)
{
    for (MySerialPort* port : this->serialPort)
    {
        if (port->serialPort.portName() == portName)
        {
            if (port->serialPort.isOpen())
            {
                port->serialPort.close();
            }
        }
    }
}

void TeminalPlugin::FlushSerialPortCfg(const QVariantMap& config)
{
    for (MySerialPort* port : this->serialPort)
    {
        if (port->serialPort.portName() == config.value("portName").toString())
        {
            port->portName         = config.value("portName").toString();
            port->isSelect         = config.value("isSelect").toBool();
            port->isOpen           = config.value("isOpen").toBool();
            port->baudRate         = config.value("baudRate").toString();
            port->dataBits         = config.value("dataBits").toString();
            port->stopBits         = config.value("stopBits").toString();
            port->parityBits       = config.value("parityBits").toString();
            port->timeStamp        = config.value("timeStamp").toBool();
            port->rollDisable      = config.value("rollDisable").toBool();
            port->hexReception     = config.value("hexReception").toBool();
            port->hexLineFeed      = config.value("hexLineFeed").toBool();
            port->hexSending       = config.value("hexSending").toBool();
            port->autoLineFeed     = config.value("autoLineFeed").toBool();
            port->isAutoSend       = config.value("isAutoSend").toBool();
            port->autoSendInterval = config.value("autoSendInterval").toString();
            port->pattern          = config.value("pattern").toInt();

            // Reopen port
            if (port->serialPort.isOpen())
            {
                port->serialPort.close();
                this->OpenSerialPort(port->portName);
            }

            break;
        }
    }
}

void TeminalPlugin::SerialPortTx(QString portName, QString text) { emit this->SerialPortTxRequested(portName, text); }

void TeminalPlugin::AddNewLine(QString portName, QString lineName)
{
    for (MySerialPort* port : this->serialPort)
    {
        if (port->serialPort.portName() == portName)
        {
            port->AddLine(lineName);
        }
    }
}

void TeminalPlugin::DeleteAllLine(QString portName)
{
    for (MySerialPort* port : this->serialPort)
    {
        if (port->serialPort.portName() == portName)
        {
            port->line.clear();
        }
    }
}

void TeminalPlugin::HandleOpenSerialPort(const QString& portName)
{
    for (MySerialPort* port : this->serialPort)
    {
        if (port->serialPort.portName() == portName)
        {
            port->serialPort.setBaudRate(port->baudRate.toInt());
            port->serialPort.setDataBits((QSerialPort::DataBits)port->dataBits.toInt());
            port->serialPort.setStopBits((QSerialPort::StopBits)port->stopBits.toInt());
            port->serialPort.setParity(
                (port->parityBits == "无") ?
                    QSerialPort::NoParity :
                    ((port->parityBits == "奇校验") ? QSerialPort::OddParity : QSerialPort::EvenParity));
            if (!port->serialPort.open(QIODevice::ReadWrite)) {}
        }
    }
}

void TeminalPlugin::HandleSerialPortTx(QString portName, QString text)
{
    int     icnt = 0;
    QString tmpString;

    for (MySerialPort* port : this->serialPort)
    {
        if (port->serialPort.isOpen() && port->serialPort.portName() == portName)
        {
            QByteArray data;
            if (port->pattern == 1)
            {
                if (text == "16777219")
                {
                    text = "\x08";
                }
                else if (text == "16777234")
                {
                    text = "\x1B[D";
                }
                else if (text == "16777235")
                {
                    text = "\x1B[A";
                }
                else if (text == "16777236")
                {
                    text = "\x1B[C";
                }
                else if (text == "16777237")
                {
                    text = "\x1B[B";
                }
                else if (text == "32")
                {
                    text = "\u0020";
                }
                data = text.toUtf8();
            }
            else
            {
                data = text.toUtf8();
                if (port->autoLineFeed)
                {
                    data.append("\n");
                }
                if (port->hexSending)
                {
                    for (; icnt < data.length(); icnt++)
                    {
                        tmpString = QString::number(data[icnt], 16).toUpper();
                        data.remove(icnt, 1);                   // 删除当前字符
                        data.insert(icnt, tmpString.toUtf8());  // 插入16进制字符
                        if (tmpString.length() == 1)
                        {
                            data.insert(icnt, '0');
                        }
                        icnt += 2;
                        data.insert(icnt, ' ');  // 插入空格
                    }
                }
            }
            port->serialPort.write(data);
            QMetaObject::invokeMethod(qmlObj, "portTxRxNum", QVariant::fromValue(port->serialPort.portName()),
                                      QVariant::fromValue(data.length()), QVariant::fromValue(0));
        }
    }
}
